package se.webbzon.boltzmann.menu;

import java.awt.image.ImageObserver;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.SortedSet;
import java.util.TreeSet;

import se.webbzon.oschi01.worldcamera.WorldCameraPane;

public abstract class MPage {
	
	// The menu item listener associated with this page
	private final MItemListener listener;
	
	// The image observer of this game menu page
	private final ImageObserver observer;
	
	// The parent of this game menu page
	private final MPage parent;
	
	// A collection of all the items of this menu page
	private final SortedSet<MItem> items;
	
	// The name of the game menu page
	private final String name;
	
	// A collection of all the listeners to this menu page
	private final Collection<MPageListener> listeners;
	
	/*============================================================
	Constructors
	============================================================*/
	
	/** Creates a new game menu page with the given layout but without
	 * a parent page. **/
	public MPage(ImageObserver observer, String name) {
		this(observer,null,name);
	}
	
	/** Creates a new game menu page with the given layout and 
	 * parent page. **/
	public MPage(ImageObserver observer, MPage parent, String name) {
		this.observer = observer;
		this.parent = parent;
		this.name = name;
		items = Collections.synchronizedSortedSet(new TreeSet<MItem>(new DepthComparator()));
		listener = new MItemListenerImpl();
		listeners = new ArrayList<MPageListener>();
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** This method is invoked when the menu page needs to redo its layout. **/
	public abstract void onRedoLayout();
	
	/** This method is invoked when the menu page is entered. **/
	public abstract void onEnter(GameMenu menu);
	
	/** This method is invoked when the menu page is left. **/
	public abstract void onLeave(GameMenu menu);
	
	/** Adds a listener to this menu page. **/
	public boolean addListener(MPageListener listener) {
		return listeners.add(listener);
	}
	
	/** Removes a listener from this menu page. **/
	public boolean removeListener(MPageListener listener) {
		return listeners.remove(listener);
	}
	
	/** Clears all the listeners from this menu page. **/
	public void clearListeners() {
		listeners.clear();
	}
	
	/** Returns the image observer of this menu page. **/
	public ImageObserver getImageObserver() {
		return observer;
	}
	
	/** Returns the name of the menu page. **/
	public String getName() {
		return name;
	}
	
	/** Returns true if this page has a parent page. **/
	public boolean hasParent() {
		return parent != null;
	}
	
	/** Returns the parent of this menu page. **/
	public MPage getParent() {
		return parent;
	}
	
	/** Returns true if the given menu item was added to the menu page. **/
	public boolean addItem(MItem item) {
		if (items.add(item)) {
			item.addListener(listener);
			for (MPageListener listener : listeners.toArray(new MPageListener[0]))
				listener.onItemAdded(this, item);
			return true;
		} else
			return false;
	}
	
	/** Returns true if the given menu item was removed to the menu page. **/
	public boolean removeItem(MItem item) {
		if (items.remove(item)) {
			item.removeListener(listener);
			for (MPageListener listener : listeners.toArray(new MPageListener[0]))
				listener.onItemRemoved(this, item);
			return true;
		} else
			return false;
	}
	
	/** Removes all the menu item from the menu page. **/
	public void removeAll() {
		synchronized (items) {
			for (MItem item : items)
				item.removeListener(listener);
		}
		items.clear();
		for (MPageListener listener : listeners.toArray(new MPageListener[0]))
			listener.onClear(this);
	}
	
	/** Returns the number of items stored in this menu page. **/
	public int items() {
		return items.size();
	}
	
	/** Returns the (n+1):th item stored in this page. The order is
	 * not guaranteed to persist if new items are added to the page. **/
	public MItem getItem(int n) {
		int index = 0;
		synchronized (items) {
			for (MItem item : items) {
				if (index == n)
					return item;
				else
					index++;
			}
		}
		return null;
	}
	
	/** Draws the menu page on the provided world camera pane. **/
	public final void onDraw(GameMenu menu, WorldCameraPane hud) {
		synchronized (items) {
			for (MItem item : items)  {
				if (item.isVisible())
					item.onDraw(menu, hud);
			}
		}
	}
	
	/*============================================================
	Private Classes
	============================================================*/
	
	/** A menu item listener. **/
	private final class MItemListenerImpl implements MItemListener {

		@Override public void onChangeSize(MItem item) {
			onRedoLayout();
		}
		
	}
	
	/** A comparator used to sort the menu items in the page. **/
	private final class DepthComparator implements Comparator<MItem> {

		@Override public int compare(MItem left, MItem right) {
			int order = right.getDepth() - left.getDepth();
			if (order == 0) {
				return left.hashCode() - right.hashCode();
			} else
				return order;
		}
		
	}

}
